home *** CD-ROM | disk | FTP | other *** search
/ ADA Programming Guide / ADA Programming Guide.iso / ada_gnu / adainc / a-stwima.adb < prev    next >
Text File  |  1996-01-30  |  16KB  |  583 lines

  1. ------------------------------------------------------------------------------
  2. --                                                                          --
  3. --                         GNAT RUNTIME COMPONENTS                          --
  4. --                                                                          --
  5. --                A D A . S T R I N G S . W I D E _ M A P S                 --
  6. --                                                                          --
  7. --                                 B o d y                                  --
  8. --                                                                          --
  9. --                            $Revision: 1.6 $                              --
  10. --                                                                          --
  11. --           Copyright (c) 1992,1993,1994 NYU, All Rights Reserved          --
  12. --                                                                          --
  13. -- The GNAT library is free software; you can redistribute it and/or modify --
  14. -- it under terms of the GNU Library General Public License as published by --
  15. -- the Free Software  Foundation; either version 2, or (at your option) any --
  16. -- later version.  The GNAT library is distributed in the hope that it will --
  17. -- be useful, but WITHOUT ANY WARRANTY;  without even  the implied warranty --
  18. -- of MERCHANTABILITY  or  FITNESS FOR  A PARTICULAR PURPOSE.  See the  GNU --
  19. -- Library  General  Public  License for  more  details.  You  should  have --
  20. -- received  a copy of the GNU  Library  General Public License  along with --
  21. -- the GNAT library;  see the file  COPYING.LIB.  If not, write to the Free --
  22. -- Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.        --
  23. --                                                                          --
  24. ------------------------------------------------------------------------------
  25.  
  26. package body Ada.Strings.Wide_Maps is
  27.  
  28.    ---------
  29.    -- "=" --
  30.    ---------
  31.  
  32.    --  The sorted, discontiguous form is canonical, so equality can be used
  33.  
  34.    function "=" (Left, Right : in Wide_Character_Set) return Boolean is
  35.    begin
  36.       return Left.all = Right.all;
  37.    end "=";
  38.  
  39.    ---------
  40.    -- "-" --
  41.    ---------
  42.  
  43.    function "-"
  44.      (Left, Right : in Wide_Character_Set)
  45.       return        Wide_Character_Set
  46.    is
  47.       Result : Wide_Character_Ranges (1 .. Left'Length + Right'Length);
  48.       --  Each range on the right can generate at least one more range in
  49.       --  the result, by splitting one of the left operand ranges.
  50.  
  51.       N : Natural := 0;
  52.       R : Natural := 1;
  53.       W : Wide_Character;
  54.  
  55.    begin
  56.       --  Basic loop is through ranges of left set
  57.  
  58.       for L in Left'Range loop
  59.  
  60.          --  W is lowest element of current left range not dealt with yet
  61.  
  62.          W := Left (L).Low;
  63.  
  64.          --  Skip by ranges of right set that have no impact on us
  65.  
  66.          while R <= Right'Length and then Right (R).High < W loop
  67.             R := R + 1;
  68.          end loop;
  69.  
  70.          --  Deal with ranges on right that create holes in the left range
  71.  
  72.          while R <= Right'Length and then Right (R).High < Left (L).High loop
  73.             N := N + 1;
  74.             Result (N).Low  := W;
  75.             Result (N).High := Right (R).High;
  76.             R := R + 1;
  77.          end loop;
  78.  
  79.          --  Now we have to output the final piece of the left range if any
  80.  
  81.          if R <= Right'Length and then Right (R).Low <= Left (L).High then
  82.  
  83.             --  Current right range consumes all of the rest of left range
  84.  
  85.             if Right (R).Low < W then
  86.                null;
  87.  
  88.             --  Current right range consumes part of the rest of left range
  89.  
  90.             else
  91.                N := N + 1;
  92.                Result (N).Low  := W;
  93.                Result (N).High := Wide_Character'Pred (Right (R).Low);
  94.             end if;
  95.  
  96.          --  Rest of left range to be retained complete
  97.  
  98.          else
  99.             N := N + 1;
  100.             Result (N).Low  := W;
  101.             Result (N).High := Left (L).High;
  102.          end if;
  103.       end loop;
  104.  
  105.       return new Wide_Character_Ranges'(Result (1 .. N));
  106.    end "-";
  107.  
  108.    -----------
  109.    -- "and" --
  110.    -----------
  111.  
  112.    function "and"
  113.      (Left, Right : in Wide_Character_Set)
  114.       return        Wide_Character_Set
  115.    is
  116.       Result : Wide_Character_Ranges (1 .. Right.all'Length + 1);
  117.       N      : Natural := 0;
  118.       L, R   : Natural := 1;
  119.  
  120.    begin
  121.       --  Loop to search for overlapping character ranges
  122.  
  123.       loop
  124.          exit when L > Left.all'Last;
  125.          exit when R > Right.all'Last;
  126.  
  127.          if Left (L).High < Right (R).Low then
  128.             L := L + 1;
  129.  
  130.          elsif Right (R).High < Left (L).Low then
  131.             R := R + 1;
  132.  
  133.          --  Here we have Left.High  >= Right.Low
  134.          --           and Right.High >= Left.Low
  135.          --  so we have an overlapping range
  136.  
  137.          else
  138.             N := N + 1;
  139.             Result (N).Low :=
  140.               Wide_Character'Max (Left (L).Low,  Right (R).Low);
  141.             Result (N).High :=
  142.               Wide_Character'Min (Left (L).High, Right (R).High);
  143.             L := L + 1;
  144.             R := R + 1;
  145.          end if;
  146.       end loop;
  147.  
  148.       return new Wide_Character_Ranges'(Result (1 .. N));
  149.    end "and";
  150.  
  151.    -----------
  152.    -- "not" --
  153.    -----------
  154.  
  155.    function "not"
  156.      (Right  : in Wide_Character_Set)
  157.       return Wide_Character_Set
  158.    is
  159.       Result : Wide_Character_Ranges (1 .. Right.all'Length + 1);
  160.       N      : Natural := 0;
  161.  
  162.    begin
  163.       if Right (1).Low /= Wide_Character'First then
  164.          N := N + 1;
  165.          Result (N).Low  := Wide_Character'First;
  166.          Result (N).High := Wide_Character'Pred (Right (1).Low);
  167.       end if;
  168.  
  169.       for K in 1 .. Right.all'Last - 1 loop
  170.          N := N + 1;
  171.          Result (N).Low  := Wide_Character'Succ (Right (K).High);
  172.          Result (N).High := Wide_Character'Pred (Right (K + 1).Low);
  173.       end loop;
  174.  
  175.       if Right (Right.all'Last).High /= Wide_Character'Last then
  176.          N := N + 1;
  177.          Result (N).Low  := Wide_Character'Succ (Right (Right'Last).High);
  178.          Result (N).High := Wide_Character'Pred (Right (1).Low);
  179.       end if;
  180.  
  181.       return new Wide_Character_Ranges'(Result (1 .. N));
  182.    end "not";
  183.  
  184.    ----------
  185.    -- "or" --
  186.    ----------
  187.  
  188.    function "or"
  189.      (Left, Right : in Wide_Character_Set)
  190.       return        Wide_Character_Set
  191.    is
  192.       Result : Wide_Character_Ranges (1 .. Left'Length + Right'Length);
  193.       N      : Natural;
  194.       L, R   : Natural;
  195.  
  196.    begin
  197.       if Left'Length = 0 then
  198.          return Right;
  199.  
  200.       elsif Right'Length = 0 then
  201.          return Left;
  202.  
  203.       else
  204.          N := 1;
  205.          Result (1) := Left (1);
  206.          L := 2;
  207.          R := 1;
  208.  
  209.          loop
  210.             --  Collapse next left range into current result range if possible
  211.  
  212.             if L <= Left'Length
  213.               and then Wide_Character'Pos (Left (L).Low) <=
  214.                        Wide_Character'Pos (Result (N).High) + 1
  215.             then
  216.                Result (N).High :=
  217.                  Wide_Character'Max (Result (N).High, Left (L).High);
  218.                L := L + 1;
  219.  
  220.             --  Collapse next right range into current result range if possible
  221.  
  222.             elsif R <= Right'Length
  223.               and then Wide_Character'Pos (Right (R).Low) <=
  224.                        Wide_Character'Pos (Result (N).High) + 1
  225.             then
  226.                Result (N).High :=
  227.                  Wide_Character'Max (Result (N).High, Right (R).High);
  228.                R := R + 1;
  229.  
  230.             --  Otherwise establish new result range
  231.  
  232.             else
  233.                if L <= Left'Length then
  234.                   N := N + 1;
  235.                   Result (N) := Left (L);
  236.                   L := L + 1;
  237.  
  238.                elsif R <= Right'Length then
  239.                   N := N + 1;
  240.                   Result (N) := Right (R);
  241.                   R := R + 1;
  242.  
  243.                else
  244.                   exit;
  245.                end if;
  246.             end if;
  247.          end loop;
  248.       end if;
  249.  
  250.       return new Wide_Character_Ranges'(Result (1 .. N));
  251.    end "or";
  252.  
  253.    -----------
  254.    -- "xor" --
  255.    -----------
  256.  
  257.    function "xor"
  258.      (Left, Right : in Wide_Character_Set)
  259.       return        Wide_Character_Set
  260.    is
  261.       Result : Wide_Character_Ranges (1 .. Left'Length + Right'Length);
  262.       N      : Natural := 0;
  263.       L, R   : Natural := 1;
  264.  
  265.    begin
  266.       return (Left or Right) - (Left and Right);
  267.    end "xor";
  268.  
  269.    -----------
  270.    -- Is_In --
  271.    -----------
  272.  
  273.    function Is_In
  274.      (Element : in Wide_Character;
  275.       Set     : in Wide_Character_Set)
  276.       return    Boolean
  277.    is
  278.       L, R, M : Natural;
  279.  
  280.    begin
  281.       L := Set'First;
  282.       R := Set'Last;
  283.  
  284.       --  Binary search loop. The invariant is that if Element is in any of
  285.       --  of the constituent ranges it is in one between Set (L) and Set (R).
  286.  
  287.       loop
  288.          if L > R then
  289.             return False;
  290.  
  291.          else
  292.             M := (L + R) / 2;
  293.  
  294.             if Element > Set (M).High then
  295.                L := M + 1;
  296.             elsif Element < Set (M).Low then
  297.                R := M - 1;
  298.             else
  299.                return True;
  300.             end if;
  301.          end if;
  302.       end loop;
  303.    end Is_In;
  304.  
  305.    ---------------
  306.    -- Is_Subset --
  307.    ---------------
  308.  
  309.    function Is_Subset
  310.      (Elements : in Wide_Character_Set;
  311.       Set      : in Wide_Character_Set)
  312.       return     Boolean
  313.    is
  314.       S : Positive := 1;
  315.       E : Positive := 1;
  316.  
  317.    begin
  318.       loop
  319.          --  If no more element ranges, done, and result is true
  320.  
  321.          if E > Elements'Length then
  322.             return True;
  323.  
  324.          --  If more element ranges, but no more set ranges, result is false
  325.  
  326.          elsif S > Set'Length then
  327.             return False;
  328.  
  329.          --  Remove irrelevant set range
  330.  
  331.          elsif Set (S).High < Elements (E).Low then
  332.             S := S + 1;
  333.  
  334.          --  Get rid of element range that is properly covered by set
  335.  
  336.          elsif Set (S).Low <= Elements (E).Low
  337.             and then Elements (E).High <= Set (S).High
  338.          then
  339.             E := E + 1;
  340.  
  341.          --  Otherwise we have a non-covered element range, result is false
  342.  
  343.          else
  344.             return False;
  345.          end if;
  346.       end loop;
  347.    end Is_Subset;
  348.  
  349.    ---------------
  350.    -- To_Domain --
  351.    ---------------
  352.  
  353.    function To_Domain
  354.      (Map  : in Wide_Character_Mapping)
  355.       return Wide_Character_Sequence
  356.    is
  357.    begin
  358.       return Map.Domain.all;
  359.    end To_Domain;
  360.  
  361.    ----------------
  362.    -- To_Mapping --
  363.    ----------------
  364.  
  365.    function To_Mapping
  366.      (From, To : in Wide_Character_Sequence)
  367.       return     Wide_Character_Mapping
  368.    is
  369.       Domain : Wide_Character_Sequence (1 .. From'Length);
  370.       Rangev : Wide_Character_Sequence (1 .. To'Length);
  371.       N      : Natural := 0;
  372.       K      : Natural := 0;
  373.  
  374.    begin
  375.       if From'Length /= To'Length then
  376.          raise Translation_Error;
  377.  
  378.       else
  379.          for J in From'Range loop
  380.             for M in 1 .. N loop
  381.                if From (J) = Domain (M) then
  382.                   raise Translation_Error;
  383.                elsif From (J) < Domain (M) then
  384.                   Domain (M + 1 .. N + 1) := Domain (M .. N);
  385.                   Domain (M) := From (J);
  386.                   Rangev (M) := To   (J);
  387.                   goto Continue;
  388.                end if;
  389.             end loop;
  390.  
  391.             Domain (N + 1) := From (J);
  392.             Rangev (N + 1) := To   (J);
  393.  
  394.             <<Continue>>
  395.                N := N + 1;
  396.          end loop;
  397.  
  398.          return (Domain => new Wide_Character_Sequence'(Domain (1 .. N)),
  399.                  Rangev => new Wide_Character_Sequence'(Rangev (1 .. N)));
  400.       end if;
  401.    end To_Mapping;
  402.  
  403.    --------------
  404.    -- To_Range --
  405.    --------------
  406.  
  407.    function To_Range
  408.      (Map  : in Wide_Character_Mapping)
  409.       return Wide_Character_Sequence
  410.    is
  411.    begin
  412.       return Map.Rangev.all;
  413.    end To_Range;
  414.  
  415.    ---------------
  416.    -- To_Ranges --
  417.    ---------------
  418.  
  419.    function To_Ranges
  420.      (Set :  in Wide_Character_Set)
  421.       return Wide_Character_Ranges
  422.    is
  423.    begin
  424.       return Set.all;
  425.    end To_Ranges;
  426.  
  427.    -----------------
  428.    -- To_Sequence --
  429.    -----------------
  430.  
  431.    function To_Sequence
  432.      (Set  : in Wide_Character_Set)
  433.       return Wide_Character_Sequence
  434.    is
  435.       Result : Wide_String (Positive range 1 .. 2 ** 16);
  436.       N      : Natural := 0;
  437.  
  438.    begin
  439.       for J in Set'Range loop
  440.          for K in Set (J).Low .. Set (J).High loop
  441.             N := N + 1;
  442.             Result (N) := K;
  443.          end loop;
  444.       end loop;
  445.  
  446.       return Result (1 .. N);
  447.    end To_Sequence;
  448.  
  449.    ------------
  450.    -- To_Set --
  451.    ------------
  452.  
  453.    --  Case of multiple range input
  454.  
  455.    function To_Set
  456.      (Ranges : in Wide_Character_Ranges)
  457.       return   Wide_Character_Set
  458.    is
  459.       Result : Wide_Character_Ranges (Ranges'Range);
  460.       N      : Natural := 0;
  461.       J      : Natural;
  462.  
  463.    begin
  464.       --  The output of To_Set is required to be sorted by increasing Low
  465.       --  values, and discontiguous, so first we sort them as we enter them,
  466.       --  using a simple insertion sort.
  467.  
  468.       for J in Ranges'Range loop
  469.          for K in 1 .. N loop
  470.             if Ranges (J).Low < Result (K).Low then
  471.                Result (K + 1 .. N + 1) := Result (K .. N);
  472.                Result (K) := Ranges (J);
  473.                goto Continue;
  474.             end if;
  475.          end loop;
  476.  
  477.          Result (N + 1) := Ranges (J);
  478.  
  479.          <<Continue>>
  480.             N := N + 1;
  481.       end loop;
  482.  
  483.       --  Now collapse any contiguous or overlapping ranges
  484.  
  485.       J := 1;
  486.       while J < N loop
  487.          if Wide_Character'Pos (Result (J).High) + 1 >=
  488.             Wide_Character'Pos (Result (J + 1).Low)
  489.          then
  490.             Result (J).High :=
  491.               Wide_Character'Max (Result (J).High, Result (J + 1).High);
  492.  
  493.             N := N - 1;
  494.             Result (J + 1 .. N) := Result (J + 2 .. N + 1);
  495.  
  496.          else
  497.             J := J + 1;
  498.          end if;
  499.       end loop;
  500.  
  501.       return new Wide_Character_Ranges'(Result (1 .. N));
  502.  
  503.    end To_Set;
  504.  
  505.    --  Case of single range input
  506.  
  507.    function To_Set
  508.      (Span : in Wide_Character_Range)
  509.       return Wide_Character_Set
  510.    is
  511.    begin
  512.       return new Wide_Character_Ranges'(1 => Span);
  513.    end To_Set;
  514.  
  515.    --  Case of wide string input
  516.  
  517.    function To_Set
  518.      (Sequence  : in Wide_Character_Sequence)
  519.       return      Wide_Character_Set
  520.    is
  521.       R : Wide_Character_Ranges (1 .. Sequence'Length);
  522.  
  523.    begin
  524.       for J in R'Range loop
  525.          R (J) := (Sequence (J), Sequence (J));
  526.       end loop;
  527.  
  528.       return To_Set (R);
  529.    end To_Set;
  530.  
  531.    --  Case of single wide character input
  532.  
  533.    function To_Set
  534.      (Singleton : in Wide_Character)
  535.       return      Wide_Character_Set
  536.    is
  537.    begin
  538.       return new Wide_Character_Ranges'(1 => (Singleton, Singleton));
  539.    end To_Set;
  540.  
  541.    -----------
  542.    -- Value --
  543.    -----------
  544.  
  545.    function Value
  546.      (Map     : in Wide_Character_Mapping;
  547.       Element : in Wide_Character)
  548.       return    Wide_Character
  549.    is
  550.       L, R, M : Natural;
  551.  
  552.    begin
  553.       L := 1;
  554.       R := Map.Domain'Last;
  555.  
  556.       --  Binary search loop
  557.  
  558.       loop
  559.          --  If not found, identity
  560.  
  561.          if L > R then
  562.             return Element;
  563.  
  564.          --  Otherwise do binary divide
  565.  
  566.          else
  567.             M := (L + R) / 2;
  568.  
  569.             if Element < Map.Domain (M) then
  570.                R := M - 1;
  571.  
  572.             elsif Element > Map.Domain (M) then
  573.                L := M + 1;
  574.  
  575.             else --  Element = Map.Domain (M) then
  576.                return Map.Rangev (M);
  577.             end if;
  578.          end if;
  579.       end loop;
  580.    end Value;
  581.  
  582. end Ada.Strings.Wide_Maps;
  583.